package com.ias.log.aop;

import com.alibaba.fastjson.JSON;
import com.ias.common.result.Result;
import com.ias.log.annotation.LogAnnotation;
import com.ias.log.enums.LogOptTypeEnum;
import com.ias.log.model.OperationLog;
import com.ias.log.service.LogService;
import com.ias.log.util.ExcpUtil;
import com.ias.log.util.TraceUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.scheduling.annotation.Async;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * 操作日志AOP切面
 * 
 * @author ias
 * @since 2024
 */
@Slf4j
@Aspect
@Order(-1)
public class LogAnnotationAOP {

    @Autowired(required = false)
    private LogService logService;

    /**
     * 环绕通知，记录操作日志
     *
     * @param joinPoint     切点
     * @param logAnnotation 日志注解
     * @return 方法执行结果
     * @throws Throwable 异常
     */
    @Around("@annotation(logAnnotation)")
    public Object around(ProceedingJoinPoint joinPoint, LogAnnotation logAnnotation) throws Throwable {
        // 开始时间
        long startTime = System.currentTimeMillis();
        
        // 创建操作日志对象
        OperationLog operationLog = new OperationLog();
        
        // 设置追踪ID
        String traceId = TraceUtil.getTrace();
        operationLog.setTraceId(traceId);
        operationLog.setStartTime(new Date(startTime));
        
        // 设置基本信息
        setBasicInfo(operationLog, logAnnotation);
        
        // 设置请求信息
        setRequestInfo(operationLog, joinPoint, logAnnotation);
        
        Object result = null;
        try {
            // 执行目标方法
            result = joinPoint.proceed();
            
            // 设置成功状态
            operationLog.setFlag(true);
            
            // 记录响应数据
            if (logAnnotation.recordResponseDate()) {
                setResponseData(operationLog, result);
            }
            
            // 检查响应状态
            checkResponseStatus(operationLog, result);
            
        } catch (Throwable e) {
            // 设置失败状态
            operationLog.setFlag(false);
            operationLog.setErrorMsg(ExcpUtil.getSubStackTrace(e));
            log.error("操作日志AOP异常: ", e);
            throw e;
        } finally {
            // 设置结束时间和执行时长
            long endTime = System.currentTimeMillis();
            operationLog.setEndTime(new Date(endTime));
            operationLog.setDuration(endTime - startTime);
            operationLog.setCreateTime(new Date());
            
            // 异步保存日志
            if (logAnnotation.isSave()) {
                saveLogAsync(operationLog);
            }
            
            // 打印日志
            printLog(operationLog);
        }
        
        return result;
    }

    /**
     * 设置基本信息
     */
    private void setBasicInfo(OperationLog operationLog, LogAnnotation logAnnotation) {
        operationLog.setModule(logAnnotation.module());
        operationLog.setRemark(logAnnotation.remark());
        operationLog.setOptType(logAnnotation.optType());
        operationLog.setOptTypeName(LogOptTypeEnum.getOptTypeNameByOptType(logAnnotation.optType()));
        operationLog.setLogType(Integer.valueOf(logAnnotation.logType()));
    }

    /**
     * 设置请求信息
     */
    private void setRequestInfo(OperationLog operationLog, ProceedingJoinPoint joinPoint, LogAnnotation logAnnotation) {
        try {
            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            if (attributes != null) {
                HttpServletRequest request = attributes.getRequest();
                
                // 设置请求路径和IP
                operationLog.setPath(request.getRequestURI());
                operationLog.setIp(getIpAddress(request));
                operationLog.setMethod(request.getMethod());
                
                // 从请求头获取额外信息
                operationLog.setClientId(request.getHeader("clientId"));
                operationLog.setSysServiceId(request.getHeader("sysServiceId"));
                
                String serviceType = request.getHeader("serviceType");
                if (StringUtils.isNotBlank(serviceType)) {
                    try {
                        operationLog.setServiceType(Integer.parseInt(serviceType));
                    } catch (NumberFormatException ignored) {
                    }
                }
            }
            
            // 记录请求参数
            if (logAnnotation.recordRequestParam()) {
                setRequestParams(operationLog, joinPoint);
            }
            
            // 设置方法信息
            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
            String className = signature.getDeclaringTypeName();
            String methodName = signature.getName();
            operationLog.setServiceName(className + "." + methodName);
            
        } catch (Exception e) {
            log.error("设置请求信息失败: ", e);
        }
    }

    /**
     * 设置请求参数
     */
    private void setRequestParams(OperationLog operationLog, ProceedingJoinPoint joinPoint) {
        try {
            Object[] args = joinPoint.getArgs();
            List<Object> paramList = new ArrayList<>();
            
            for (Object arg : args) {
                // 过滤掉不需要记录的参数类型
                if (arg instanceof HttpServletRequest || arg instanceof HttpServletResponse) {
                    continue;
                }
                paramList.add(arg);
            }
            
            if (!paramList.isEmpty()) {
                String params = JSON.toJSONString(paramList);
                // 限制参数长度，避免过长
                if (params.length() > 2000) {
                    params = params.substring(0, 2000) + "...";
                }
                operationLog.setParams(params);
            }
        } catch (Exception e) {
            log.error("记录请求参数失败: ", e);
            operationLog.setParams("参数序列化失败");
        }
    }

    /**
     * 设置响应数据
     */
    private void setResponseData(OperationLog operationLog, Object result) {
        try {
            if (result == null) {
                return;
            }
            
            String resultData;
            if (result instanceof ResponseEntity) {
                ResponseEntity<?> responseEntity = (ResponseEntity<?>) result;
                String filename = responseEntity.getHeaders().getContentDisposition().getFilename();
                if (StringUtils.isNotBlank(filename)) {
                    resultData = "文件: " + filename;
                } else {
                    resultData = JSON.toJSONString(responseEntity.getBody());
                }
            } else {
                resultData = JSON.toJSONString(result);
            }
            
            // 限制响应数据长度
            if (resultData.length() > 2000) {
                resultData = resultData.substring(0, 2000) + "...";
            }
            operationLog.setResultData(resultData);
            
        } catch (Exception e) {
            log.error("记录响应数据失败: ", e);
            operationLog.setResultData("响应数据序列化失败");
        }
    }

    /**
     * 检查响应状态
     */
    private void checkResponseStatus(OperationLog operationLog, Object result) {
        if (result instanceof ResponseEntity) {
            ResponseEntity<?> responseEntity = (ResponseEntity<?>) result;
            if (responseEntity.getStatusCode() != HttpStatus.OK) {
                operationLog.setFlag(false);
            }
        } else if (result instanceof Result) {
            Result<?> apiResult = (Result<?>) result;
            if (!Result.isSuccess(apiResult)) {
                operationLog.setFlag(false);
            }
        }
    }

    /**
     * 获取IP地址
     */
    private String getIpAddress(HttpServletRequest request) {
        String ip = request.getHeader("X-Forwarded-For");
        if (StringUtils.isBlank(ip) || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (StringUtils.isBlank(ip) || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (StringUtils.isBlank(ip) || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("X-Real-IP");
        }
        if (StringUtils.isBlank(ip) || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        
        // 处理多个IP的情况，取第一个
        if (StringUtils.isNotBlank(ip) && ip.contains(",")) {
            ip = ip.split(",")[0].trim();
        }
        
        return ip;
    }

    /**
     * 异步保存日志
     */
    @Async("logExecutor")
    public void saveLogAsync(OperationLog operationLog) {
        try {
            if (logService != null) {
                logService.save(operationLog);
                log.debug("操作日志保存成功: traceId={}", operationLog.getTraceId());
            } else {
                log.warn("LogService未配置，操作日志未保存");
            }
        } catch (Exception e) {
            log.error("保存操作日志失败: ", e);
        }
    }

    /**
     * 打印日志
     */
    private void printLog(OperationLog operationLog) {
        String status = operationLog.getFlag() ? "成功" : "失败";
        log.info("操作日志 - 模块: {}, 操作: {}, 描述: {}, 耗时: {}ms, 状态: {}, IP: {}, TraceId: {}",
                operationLog.getModule(),
                operationLog.getOptTypeName(),
                operationLog.getRemark(),
                operationLog.getDuration(),
                status,
                operationLog.getIp(),
                operationLog.getTraceId());
    }
}
